from cqhttp import CQHttp
import colorful
import logging
from tools.CQ_tools import group_msg_level

import sys
import subprocess
import psutil
import os

class Bot_body():
    def __init__(self,
        master_qqs = {892132762,},
        report_qq = 892132762,
        api_root = 'http://localhost:5700',
        access_token='1482766065',
        secret='hijklmn',
        host='localhost',
        port=8080,
        version = "1.2",
        version_scription="添加多个master_qqs"
    ):
        self.master_qqs = master_qqs
        self.report_qq = report_qq
        self.api_root = api_root
        self.access_token=access_token
        self.secret = secret
        self.host = host
        self.port = port
        self.bot = CQHttp(
            api_root = self.api_root,
            access_token = self.access_token,
            secret = self.secret
        )
        self.on_type_funcs = {
            'message':[],
            'request':[],
            'notice':[]
        }
        self.func_infos = dict()
        # self.func_calling_way = 'async' # 'async', 'sync'
        self.func_calling_way = 'sync' # 'async', 'sync'
        self.version = version
        init_msg = "机器人初始化中，版本号：{}".format(self.version)
        if version_scription!="":
            init_msg = init_msg+"【{}】".format(version_scription)
        self.bot.send_private_msg(user_id=self.report_qq, message=init_msg)
        self.help_msg = r"""【机器人管理帮助】
0. 机器人帮助：显示本条消息
1. 机器人重启
2. 机器人更新：从git目录clone下最新代码后重启机器人
3. 机器人关机：（慎用！）"""

        def wrap_bot_send(send, bot):
            def send_with_try(context, message, **kwargs):
                try:
                    logging.debug("try to send with message=null")
                    return send(context, message, **kwargs)
                except:
                    context=context.copy()
                    context['message'] = message
                    context.update(kwargs)
                    report_msg = 'bot.send()执行错误， context={}'.format(context)
                    logging.warning(colorful.yellow(report_msg))
                    bot.send_private_msg(user_id=self.report_qq, message=report_msg, auto_escape=True)
                    return None
            return send_with_try
        self.bot.send = wrap_bot_send(self.bot.send, self.bot)
        self.register_on_type(self.handle_msg_on_restart_or_update, post_type='message', plugin_name="机器人管理")
    
    def register_on_type(self, func, post_type:str, **kwargs):
        self.func_infos[func] = kwargs
        self.on_type_funcs[post_type].append(func)
        name = "UNKNWON" if "plugin_name" not in kwargs else kwargs['plugin_name']
        logging.info(colorful.cyan("Successfully register plugin: {}".format(name)))

        
    def call_plugin(self, func, context):
        plugin_name = "UNKNOWN" if 'plugin_name' not in self.func_infos[func] \
            else self.func_infos[func]['plugin_name']
        logging.debug(colorful.cyan("call plugin: {}".format(plugin_name)))
        try:
            func(context)
        except:
            import traceback
            error_msg = traceback.format_exc()
            error_msg = "[PLUGIN:{}][Error msg]:\n{}[Context]:\n{}".format(
                plugin_name, error_msg, str(context)
            )
            logging.warning(colorful.red(error_msg))
            self.bot.send_msg(
                message_type='private',
                user_id=self.report_qq,
                message=error_msg,
                auto_escape=True
            )
        logging.debug(colorful.cyan("over plugin: {}".format(plugin_name)))

    def handle_on_type(self, context, post_type='message'):
        if self.func_calling_way == 'async':
            for func in self.on_type_funcs[post_type]:
                import threading
                t = threading.Thread(target=self.call_plugin, args=(func, context))
                t.setDaemon(True)
                t.start()
        else:
            for func in self.on_type_funcs[post_type]:
                self.call_plugin(func, context)

    def handle_msg_on_restart_or_update(self, context):
        raw_message = context['raw_message']
        msg_level = group_msg_level.judge(context, master_qqs=self.master_qqs)
        if raw_message in ["机器人关机", "机器人重启", "机器人更新"] and context['user_id'] in self.master_qqs:
            pid = os.getpid()
            cmdline = psutil.Process(pid).cmdline()
            python = sys.executable
            pcmd = [python, "./monitor.py", raw_message, str(pid)]
            pcmd.extend(cmdline[1:])
            logging.debug(colorful.yellow("pcmd={}".format(pcmd)))
            subprocess.Popen(pcmd)
            return
        elif raw_message == "机器人帮助" and context['user_id'] in self.master_qqs:
            self.bot.send(context, self.help_msg)
            return


    def run(self):
        @self.bot.on_message()
        def handle_msg(context):
            return self.handle_on_type(context, post_type='message')
        @self.bot.on_request()
        def handle_request(context):
            return self.handle_on_type(context, post_type='request')
        @self.bot.on_notice()
        def handle_notice(context):
            return self.handle_on_type(context, post_type='notice')
                
        logging.debug(colorful.cyan("to call bot.run()"))
        self.bot.run(host=self.host, port=self.port)

if __name__ == '__main__':
    bot = Bot_body()
    def echo_context(bot:CQHttp, context):
        bot.send(context, "Your context(from bot::main::echo_context):" + str(context))
    bot.register_on_message(echo_context)
    bot.run()
